/* eslint-disable no-useless-escape */
const num = /[0-9]/;
const op = /[\+\-\*\/]/;
const groupStart = /[\(\{\[]/;
const groupEnd = /[\)\}\]]/;

const opPriority = {
  "(": 0,
  "{": 0,
  "[": 0,
  "-": 1,
  "+": 1,
  "/": 2,
  "*": 2,
};
function lte(op1, op2) {
  return opPriority[op1] - opPriority[op2] <= 0;
}

/**
 * 四则运算`+-/*`，三种括号`()[]{}`
 */
function arithmetic(expression) {
  const exp = suffixExpression(expression);

  const nums = [];
  for (let i = 0; i < exp.length; i++) {
    if (num.test(exp[i])) {
      nums.push(exp[i]);
    } else {
      const b = nums.pop();
      const a = nums.pop();
      nums.push(operation(a, b, exp[i]));
    }
  }

  return nums[0];
}

function operation(a, b, op) {
  if (op == "+") return a + b;
  if (op == "-") return a - b;
  if (op == "*") return a * b;
  if (op == "/") return a / b;
}

/**
 * remove all spaces
 */
function shrink(exp) {
  let res = "";
  for (const char of exp) {
    if (/\S/.test(char)) res += char;
  }
  return res;
}

/**
 * 中缀表达式 --> 后缀表达式
 * @param {string} exp
 */
function suffixExpression(exp) {
  exp = shrink(exp);
  const res = [];

  const ops = [];
  let unaryNegative = false;
  for (let i = 0; i < exp.length; i++) {
    // numbers
    let numLiteral = "";
    while (num.test(exp[i])) {
      numLiteral += exp[i];
      i++;
    }
    if (numLiteral != "") {
      // only valid `-` before number `x` will be transpile to `0-x`
      if (unaryNegative) {
        res.push(0);
        res.push(parseInt(numLiteral));
        res.push("-");
        unaryNegative = false;
      } else {
        res.push(parseInt(numLiteral));
      }
    }

    // operators
    if (op.test(exp[i])) {
      // extra 0 for prefix -
      if (
        exp[i] === "-" &&
        !num.test(exp[i - 1]) && // not a number
        !groupEnd.test(exp[i - 1]) // not the close bracket
      ) {
        if (unaryNegative) {
          // continuous - like `--` will be ignored
          unaryNegative = false;
        } else {
          unaryNegative = true;
        }
        continue;
      }
      // ignore prefix +
      if (
        exp[i] === "+" &&
        !num.test(exp[i - 1]) &&
        !groupEnd.test(exp[i - 1])
      ) {
        continue;
      }

      // binary operators
      while (ops[0] && lte(exp[i], ops[0])) {
        res.push(ops.shift());
      }
      ops.unshift(exp[i]);
    }

    // parentheses
    if (groupStart.test(exp[i])) {
      ops.unshift(exp[i]);
    } else if (groupEnd.test(exp[i])) {
      while (!groupStart.test(ops[0])) {
        res.push(ops.shift());
      }
      ops.shift();
    }
  }

  while (ops[0]) {
    res.push(ops.shift());
  }

  return res;
}

console.log(arithmetic("3 + -2 * { 1 + 2 * [ -4 / (8 - 6) + 7 ] }")); // -19
console.log(arithmetic("--+7")); // 7
console.log(arithmetic("-7")); // -7
